#第二十一章 元类编程

from collections import namedtuple

#实现一个类似 nametuple 功能的类工厂方法

def record_factory(cls_name,field_names):
    try:
        field_names = str(field_names).replace(',',' ').split() #split方法默认传None表示用空格分隔
    except AttributeError:
        pass
    field_names = tuple(field_names)
    
    def __init__(self,*args,**kwarg):
        attrs = dict(zip(self.__slots__,args))
        attrs.update(kwarg)
        for name,value in attrs.items():
            setattr(self,name,value)
    
    def __iter__(self):
        for name in self.__slots__:
            yield getattr(self,name)
        
    def __repr__(self):
        values = ', '.join('{}={!r}'.format(*i) for i in zip(self.__slots__,self))
        return '{}({})'.format(self.__class__.__name__,values)

    #定义好鸭子模型
    cls__attrs = dict(__slots__=field_names,
        __init__=__init__,
        __iter__=__iter__,
        __repr__=__repr__,

    )

    #通过type方法返回一个类,cls__attrs属性是类的__dict__属性
    return type(cls_name,(object,),cls__attrs) 

Dog = record_factory("Dog",'name weight owner')

print(Dog)

d = Dog()
#d.age = 10 #不能动态创建属性 因为创建类的时候指定了 __slots__ 属性列表
#print(d.age)

Cat = type("Cat",(object,),dict()) #这样也可以定义一个类
print(Cat)

import chp21_2evaltime #测试导入时 python解析器执行了什么: 入到模块时python会执行顶层代码(类的定义体、把函数对象绑定到对应的全局名称上)

#python中的元类 type 它可以是函数检查是哪个类也可以是类 它是大多数类的元类

#Enum ABCMeta 也是元类
from enum import Enum
from abc import ABCMeta

#查看他们的继承关系
print(Enum.__mro__) # Enum 并非type的子类
print(ABCMeta.__mro__)

'''

所有类都是type 的实例，但是元类还是type 的子类
例如ABCMeta的继承关系 : (<class 'abc.ABCMeta'>, <class 'type'>, <class 'object'>) 

'''
from chp20_2descriptor import Validated,NoBlank,Quantity
#构建描述符的元类
class EntityMeta(type):
    ''' 元类用于创建带有验证字段的业务实体'''

    def  __init__(cls,name,bases,attr_dict):        
        super().__init__(name,bases,attr_dict)
        for key,attr in cls.__dict__.items():
            if isinstance(attr,Validated):
                type_name = type(attr).__name__
                attr.storge_name = '{}#{}'.format(type_name,key)


class Entity(metaclass=EntityMeta):
    ''' 带有验证业务实体 '''

print('Entity.__mro__',Entity.__mro__)

class LineItem2(Entity): #通过继承描述符类 自动设置属性的字段名
    description = NoBlank()
    weight = Quantity()
    price = Quantity()

    def __init__(self,description,weight,price):
        self.description = description        
        # self.weight 首先访问自身有没有属性 没有就访问类属性 类属性是一个 Quantity 实例 为实例赋值会执行类方法 __set__
        self.weight = weight 
        self.price = price
    
    def subtotal(self):
        return self.weight * self.price
itm = LineItem2(111,100,200)
print(LineItem2.weight.storge_name)